package com.dhcc.bpm.modules.system.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.dhcc.bpm.common.utils.DateUtils;
import com.dhcc.bpm.modules.system.entity.SerialNumber;
import com.dhcc.bpm.modules.system.mapper.SerialNumberMapper;
import com.dhcc.bpm.modules.system.service.ISerialNumberService;
import com.dhcc.bpm.modules.system.vo.SerialNumberVO;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.mp.support.Condition;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassName SerialNumberServiceImpl
 * @Description  系统编号表 服务实现类
 * @Author sjm
 * @Date 2020/2/21 0021 15:16
 * @Version V1.0
 * @Copyright: 2020 东华软件 . All rights reserved.
 **/
@Service("opc_serial_number")
public class SerialNumberServiceImpl extends BaseServiceImpl<SerialNumberMapper, SerialNumber> implements ISerialNumberService {


	/** 格式 */
	private String pattern = "yyyyMMdd";

	/** 生成器锁 */
	private final ReentrantLock lock = new ReentrantLock();

	/** 流水号格式化器 */
	private DecimalFormat format = null;

	/** 预生成锁 */
	private final ReentrantLock prepareLock = new ReentrantLock();

	/** 最小值 */
	private int min = 0;

	/** 最大值 */
	private long max = 0;

	/** 已生成流水号（种子） */
	private long seed = min;

	/** 预生成数量 */
	private int prepare = 0;

	/** 数据库存储的当前最大序列号 **/
	long maxSerialInt = 0;

	/** 当前序列号是否为个位数自增的模式 **/
	private String isAutoIncrement = "0";

	SerialNumber sysSerialNumber = new SerialNumber();

	/** 预生成流水号 */
	HashMap<String, List<String>> prepareSerialNumberMap = new HashMap<>();

	private String DATE_SYMBOL = "%{DATE}";

	/**
	 * 自定义分页
	 * @Author sjm
	 * @Date 19:18 2020/2/21 0021
	 * @param page
	 * @param serialNumber
	 * @return com.baomidou.mybatisplus.core.metadata.IPage<com.dhcc.bpm.modules.system.vo.SerialNumberVO>
	 **/
	@Override
	public IPage<SerialNumberVO> selectSerialNumberPage(IPage<SerialNumberVO> page, SerialNumberVO serialNumber) {
		return page.setRecords(baseMapper.selectSerialNumberPage(page, serialNumber));
	}

	/**
	 * 查询单条序列号配置信息
	 *
	 * @param moduleCode 模快編碼
	 * @return
	 */
	public SerialNumber find(String moduleCode) {
		SerialNumber cond = new SerialNumber();
		cond.setModuleCode(moduleCode);
		List<SerialNumber> list = baseMapper.selectList(Condition.getQueryWrapper(cond));
		if (list.size() >= 1) {
			return list.get(0);
		}
		return null;
	}

	/**
	 * 根据模块code生成预数量的序列号存放到Map中
	 *
	 * @param moduleCode
	 *            模块code
	 * @return
	 */
	public List<String> generatePrepareSerialNumbers(String moduleCode) {
		// 临时List变量
		List<String> resultList = new ArrayList<String>(prepare);
		lock.lock();
		try {
			for (int i = 0; i < prepare; i++) {
				maxSerialInt = maxSerialInt + 1;
				if (maxSerialInt > min && (maxSerialInt + "").length() < max) {
					seed = maxSerialInt;
				} else {
					// 如果动态数字长度大于模板中的长度 例：模板CF000 maxSerialInt 1000
					seed = maxSerialInt = 0;
					// 更新数据，重置maxSerialInt为0
					sysSerialNumber = find(moduleCode);
					sysSerialNumber.setMaxSerial("0");
                    baseMapper.updateById(sysSerialNumber);
				}
				// 动态数字生成
				String formatSerialNum = format.format(seed);
				// 动态日期的生成
				if (pattern.contains(DATE_SYMBOL)) {
					String currentDate = DateUtils.dateToString("yyyyMMdd", new Date());
					formatSerialNum = formatSerialNum.replace(DATE_SYMBOL, currentDate);
				}

				resultList.add(formatSerialNum);
			}
			// 更新数据
			sysSerialNumber = find(moduleCode);
			sysSerialNumber.setMaxSerial(maxSerialInt + "");
            baseMapper.updateById(sysSerialNumber);
		} finally {
			lock.unlock();
		}
		return resultList;
	}


	/**
	 * 根据模块code生成序列号
	 *
	 * @param moduleCode
	 *            模块code
	 * @return 序列号
	 */
	@Override
	public String generateSerialNumberByModelCode(String moduleCode) {
		// 预序列号加锁
		prepareLock.lock();
		try {
			// 判断内存中是否还有序列号
			if (null != prepareSerialNumberMap.get(moduleCode) && prepareSerialNumberMap.get(moduleCode).size() > 0) {
				// 若有，返回第一个，并删除
				return prepareSerialNumberMap.get(moduleCode).remove(0);
			}
		} finally {
			// 预序列号解锁
			prepareLock.unlock();
		}
		sysSerialNumber = new SerialNumber();
		sysSerialNumber = find(moduleCode);
		// 预生成流水号数量
		prepare = Integer.parseInt(sysSerialNumber.getPreMaxNum().trim());
		// 配置模板
		pattern = sysSerialNumber.getConfigTemplet().trim();
		// 存储当前最大值
		String maxSerial = sysSerialNumber.getMaxSerial().trim();
		isAutoIncrement = sysSerialNumber.getIsAutoIncrement().trim();
		// 数据库存储的最大序列号
		maxSerialInt = Long.parseLong(maxSerial.trim());
		// 根据模板判断当前序列号数字的最大值
		max = this.counter(pattern, '0') + 1;
		if (("1").equals(isAutoIncrement)) {
			pattern = pattern.replace("0", "0");
		}
		format = new DecimalFormat(pattern);
		// 生成预序列号，存到缓存中
		List<String> resultList = generatePrepareSerialNumbers(moduleCode);
		prepareLock.lock();
		try {
			prepareSerialNumberMap.put(moduleCode, resultList);
			return prepareSerialNumberMap.get(moduleCode).remove(0);
		} finally {
			prepareLock.unlock();
		}
	}

	@Override
	public SerialNumber getSerialNumberByModelCode(String code) {
		return baseMapper.getSerialNumberByModelCode(code);
	}

	/**
	 * 最大值
	 *
	 * @param value
	 *            最大值，要求：小于等于Long.MAX_VALUE ( 9223372036854775807 )
	 * @return 流水号生成器实例
	 */
	public SerialNumberServiceImpl setMax(long value) {
		lock.lock();
		try {
			this.max = value;
		} finally {
			lock.unlock();
		}
		return this;
	}

	/**
	 * 设置最小值
	 *
	 * @param value
	 *            最小值，要求：大于等于零
	 * @return 流水号生成器实例
	 */
	public SerialNumberServiceImpl setMin(int value) {
		lock.lock();
		try {
			this.min = value;
		} finally {
			lock.unlock();
		}
		return this;
	}

	/**
	 * 设置预生成流水号数量
	 *
	 * @param count
	 *            预生成数量
	 * @return 流水号生成器实例
	 */
	public SerialNumberServiceImpl setPrepare(int count) {
		lock.lock();
		try {
			this.prepare = count;
		} finally {
			lock.unlock();
		}
		return this;
	}

	/**
	 * 统计某一个字符出现的次数
	 *
	 * @param str
	 *            查找的字符
	 * @param c
	 * @return
	 */
	private int counter(String str, char c) {
		int count = 0;
		for (int i = 0; i < str.length(); i++) {
			if (str.charAt(i) == c) {
				count++;
			}
		}
		return count;
	}

}
